package win.demonlegion.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.processing.OperationManager;
import com.qiniu.processing.OperationStatus;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.storage.persistent.FileRecorder;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.qiniu.util.StringUtils;
import com.qiniu.util.UrlSafeBase64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by MK on 2017/6/14.
 * 测试七牛云的上传
 */
public class UploadUtil {
    private static final Logger logger = LoggerFactory.getLogger(UploadUtil.class);

    private static final String TAG_SOURCE = "SOURCE";
    private static final String TAG_1080P = "1080P";
    private static final String TAG_720P = "720P";
    private static final String TAG_480P = "480P";
    private static final String TAG_360P = "360P";
    private static final String TAG_288P = "288P";

    private static final String RESOLUTION_SOURCE = "ABOVE1080p";
    private static final String RESOLUTION_1080P = "1920x1080";
    private static final String RESOLUTION_720P = "1280x720";
    private static final String RESOLUTION_480P = "854x480";
    private static final String RESOLUTION_360P = "640x360";
    private static final String RESOLUTION_288P = "512x288";

    private static final int RESOLUTION_1080P_WIDTH = 1920;
    private static final int RESOLUTION_1080P_HEIGHT = 1080;
    private static final int RESOLUTION_720P_WIDTH = 1280;
    private static final int RESOLUTION_720P_HEIGHT = 720;
    private static final int RESOLUTION_480P_WIDTH = 854;
    private static final int RESOLUTION_480P_HEIGHT = 480;
    private static final int RESOLUTION_360P_WIDTH = 640;
    private static final int RESOLUTION_360P_HEIGHT = 360;
    private static final int RESOLUTION_288P_WIDTH = 512;
    private static final int RESOLUTION_288P_HEIGHT = 288;

    private static final String persistentOpfsTemplate = "avthumb/mp4/s/%s|saveas/%s";
    // 七牛云图片和音视频信息接口名
    private static String IMAGE_INFO = "imageInfo";
    private static String AV_INFO = "avinfo";

    // 设置好账号的ACCESS_KEY和SECRET_KEY
    // 公司正式账号
//    private static String ACCESS_KEY = "PlFjKfdkDRFMN6VZy_a9h4BWBx0UbH0KYTYV8LGR";
//    private static String SECRET_KEY = "nMvYqjCiAZiKXIB05xRm3RGx1XtWO5wkAQN3Jr4X";
    // 测试账号
    private static String ACCESS_KEY = "R5fe1yqTnADgpOcyntuEhULWf7MeLAvUu2GBV5rf";
    private static String SECRET_KEY = "KigM7KYvpmoNvES3YpxnfHfDj0_ro6e5G8QVis2N";

    private final static String HTTP_PROTOCOL = "http://";
    private final static String WEIDU_DOMAIN = ".weiduapi.com";
    // 视频路径截取正则表达式
    private static final Pattern videoKeyPattern = Pattern.compile("\\w+\\.mp4");
    // 获取文件拓展名正则表达式
    private static Pattern fileTypePattern = Pattern.compile("\\.\\w+$");
    // 获取文件拓展名正则表达式
    private static Pattern keyPattern = Pattern.compile("\\d+\\.\\w+$");

    // 文件大小
    public final static int UNRESUMABLE_FILE_SIZE_LIMIT = 41943040;
    // 要上传的空间
    // 正式空间
    public final static String IMAGE_BUCKET = "images";
    public final static String VIDEO_BUCKET = "video";
    public final static String FUNCTION_BUCKET = "function";

    // 密钥配置
    private static Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
    // 自动识别要上传的空间(bucket)的存储区域是华东、华北、华南。
    private static Zone zone = Zone.autoZone();
    //构造一个带指定Zone对象的配置类
    private static Configuration cfg = new Configuration(zone);

    //设置callbackUrl以及callbackBody,七牛将文件名和文件大小回调给业务服务器

    private static String getUpToken(String bucketName) {
        switch (bucketName) {
            case IMAGE_BUCKET:
                return auth.uploadToken(bucketName,null,3600,new StringMap()
//                .put("callbackUrl",weiduPath+"/upload/callback")
                        .put("returnBody", "{\"bucket\":$(bucket),\"fname\":$(fname),\"fsize\":$(fsize)," +
                                "\"success\":$(success),\"mimeType\":$(mimeType),\"key\":$(key),\"hash\":$(hash)" +
                                "\"width\":$(imageInfo.width),\"height\":$(imageInfo.height)}")
                        .put("callbackBody","fileName=$(fname)&fileSize=$(fsize)&mimeType=$(mimeType)" +
                                "&bucket=$(bucket)&key=$(key)&hash=$(etag)&success=$(success)" +
                                "&width=$(imageInfo.width)&height=$(imageInfo.height)"));
            case VIDEO_BUCKET:
                return auth.uploadToken(bucketName,null,3600,new StringMap()
//                .put("callbackUrl",weiduPath+"/upload/callback")
                        .put("returnBody", "{\"bucket\":$(bucket),\"fname\":$(fname),\"fsize\":$(fsize)," +
                                "\"success\":$(success),\"mimeType\":$(mimeType),\"key\":$(key),\"hash\":$(hash)" +
                                "\"width\":$(avinfo.width),\"height\":$(avinfo.height)}")
                        .put("callbackBody","fileName=$(fname)&fileSize=$(fsize)&mimeType=$(mimeType)" +
                                "&bucket=$(bucket)&key=$(key)&hash=$(etag)&success=$(success)" +
                                "&width=$(avinfo.width)&height=$(avinfo.height)"));
            default:
                return auth.uploadToken(bucketName,null,3600,new StringMap()
//                .put("callbackUrl",weiduPath+"/upload/callback")
                        .put("returnBody", "{\"bucket\":$(bucket),\"fname\":$(fname),\"fsize\":$(fsize)," +
                                "\"success\":$(success),\"mimeType\":$(mimeType),\"key\":$(key),\"hash\":$(hash)}")
                        .put("callbackBody","fileName=$(fname)&fileSize=$(fsize)&mimeType=$(mimeType)" +
                                "&bucket=$(bucket)&key=$(key)&hash=$(etag)&success=$(success)"));
        }

    }

    public static String getFileKey(String fileName) {
        String formatFileName = TimestampUtil.getDatetimeString("yyyyMMddHHmmssSSS") + (int)(Math.random()*9000 +1000);
        if(fileName != null && !fileName.equals("")) {
            Matcher matcher = fileTypePattern.matcher(fileName);
            if(matcher.find())
                return formatFileName + matcher.group();
        }
        return formatFileName;
    }

    public static String getOSSKey(String fileUrl) {
        Matcher matcher = keyPattern.matcher(fileUrl);
        if(matcher.find()) return matcher.group();
        else return null;
    }

    public static JSONObject uploadFile(String bucket, String fileName, byte[] uploadBytes) {
        UploadManager uploadManager = new UploadManager(cfg);
        try{
            Response response = uploadManager.put(uploadBytes, getFileKey(fileName), getUpToken(bucket));
            return getFileUrl(response.bodyString());
        } catch (QiniuException ex) {
            logger.error(ex.error());
            return null;
        }
    }

    public static JSONObject uploadFile(String bucket, File file) {
        UploadManager uploadManager = new UploadManager(cfg);
        try{
            Response response = uploadManager.put(file, getFileKey(file.getName()), getUpToken(bucket));
            return getFileUrl(response.bodyString());
        } catch (QiniuException ex) {
            logger.error(ex.error());
            return null;
        }
    }

    public static JSONObject uploadFile(String bucket, String fileName, InputStream inputStream) {
        UploadManager uploadManager = new UploadManager(cfg);
        try{
            Response response = uploadManager.put(inputStream, getFileKey(fileName), getUpToken(bucket), null, null);
            return getFileUrl(response.bodyString());
        } catch (QiniuException ex) {
            logger.error(ex.error());
            return null;
        }
    }

    public static JSONObject resumableUploadFile(String bucket, String fileName, byte[] uploadBytes) {
        // 断点续传临时文件位置
        String localTempDir = Paths.get(System.getenv("java.io.tmpdir"), bucket).toString();
        try {
            //设置断点续传文件进度保存目录
            FileRecorder fileRecorder = new FileRecorder(localTempDir);
            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
            Response response = uploadManager.put(uploadBytes, getFileKey(fileName), getUpToken(bucket));
            return getFileUrl(response.bodyString());
        } catch (IOException ex) {
            logger.error(ex.getMessage());
            return null;
        }
    }

    public static JSONObject resumableUploadFile(String bucket, File file) {
        // 断点续传临时文件位置
        String localTempDir = Paths.get(System.getenv("java.io.tmpdir"), bucket).toString();
        try {
            //设置断点续传文件进度保存目录
            FileRecorder fileRecorder = new FileRecorder(localTempDir);
            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
            Response response = uploadManager.put(file, getFileKey(file.getName()), getUpToken(bucket));
            return getFileUrl(response.bodyString());
        } catch (IOException ex) {
            logger.error(ex.getMessage());
            return null;
        }
    }

    public static JSONObject resumableUploadFile(String bucket, String fileName, InputStream inputStream) {
        // 断点续传临时文件位置
        String localTempDir = Paths.get(System.getenv("java.io.tmpdir"), bucket).toString();
        try {
            //设置断点续传文件进度保存目录
            FileRecorder fileRecorder = new FileRecorder(localTempDir);
            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
            Response response = uploadManager.put(inputStream, getFileKey(fileName), getUpToken(bucket), null, null);
            return getFileUrl(response.bodyString());
        } catch (IOException ex) {
            logger.error(ex.getMessage());
            return null;
        }
    }

    public static JSONObject imageInfo(String fileUrl) {
        logger.debug(fileUrl);
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(fileUrl + "?" + IMAGE_INFO, JSONObject.class);
    }

    public static JSONObject avInfo(String fileUrl) {
        logger.debug(fileUrl);
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(fileUrl + "?" + AV_INFO, JSONObject.class);
    }

    private static JSONObject getFileUrl(String response) {
        JSONObject jsonObject = JSON.parseObject(response);
        String fileUrl = HTTP_PROTOCOL + jsonObject.getString("bucket") + WEIDU_DOMAIN + "/" + jsonObject.getString("key");
        jsonObject.put("url", fileUrl);
        return jsonObject;
    }

    public static String reTranscodingVideo(String url) {
        JSONObject videoInfo = new JSONObject();
        Matcher matcher = videoKeyPattern.matcher(url);
        if(matcher.find()) {
            videoInfo.put("key", matcher.group());
            JSONObject avinfo = avInfo(url);
            JSONArray jsonArray = avinfo.getJSONArray("streams");
            JSONObject videoStream = jsonArray.getJSONObject(0);
            videoInfo.put("width", videoStream.getIntValue("width"));
            videoInfo.put("height", videoStream.getIntValue("height"));

            return generateMultiResolutionVideo(videoInfo);
        }

        return null;
    }

    public static String generateMultiResolutionVideo(JSONObject videoInfo) {
        logger.debug(videoInfo.toJSONString());
        String resolution = getVideoResolution(videoInfo.getIntValue("width"), videoInfo.getIntValue("height"));
        List<String> avthumbList = new ArrayList<>();
        // 生成转码指令列表
        generateFopList(resolution, videoInfo.getString("key"), avthumbList);
        if(avthumbList.size() > 0) {
            //将多个数据处理指令拼接起来
            String persistentOpfs = StringUtils.join(avthumbList, ";");
            logger.debug(persistentOpfs);
            // 数据处理队列名称，必须
            String persistentPipeline = "corefac-transcoding";
            // 数据处理完成结果通知地址
//        String persistentNotifyUrl = "http://api.example.com/qiniu/pfop/notify";
            String persistentNotifyUrl = "";
            //构建持久化数据处理对象
            OperationManager operationManager = new OperationManager(auth, cfg);

            try {
                String persistentId = operationManager.pfop(VIDEO_BUCKET, videoInfo.getString("key"), persistentOpfs, persistentPipeline, persistentNotifyUrl, true);
                logger.debug(persistentId);
                //可以根据该 persistentId 查询任务处理进度
                return persistentId;
            } catch (QiniuException e) {
                System.err.println(e.response.toString());
            }
        }
        return null;
    }

    private static String getVideoResolution(int width, int height) {
        // 判断视频清晰度
        if(width > RESOLUTION_1080P_WIDTH && height > RESOLUTION_1080P_HEIGHT) return RESOLUTION_SOURCE;
        else if(width == RESOLUTION_1080P_WIDTH && height == RESOLUTION_1080P_HEIGHT) return RESOLUTION_1080P;
        else if(width >= RESOLUTION_720P_WIDTH && height >= RESOLUTION_720P_HEIGHT) return RESOLUTION_720P;
        else if(width >= RESOLUTION_480P_WIDTH && height >= RESOLUTION_480P_HEIGHT) return RESOLUTION_480P;
        else if(width >= RESOLUTION_360P_WIDTH && height >= RESOLUTION_360P_HEIGHT) return RESOLUTION_360P;
        else return RESOLUTION_288P;
    }

    private static String getResolutionTag(String resolution) {
        switch (resolution) {
            case RESOLUTION_SOURCE:
                return TAG_SOURCE;
            case RESOLUTION_1080P:
                return TAG_1080P;
            case RESOLUTION_720P:
                return TAG_720P;
            case RESOLUTION_480P:
                return TAG_480P;
            case RESOLUTION_360P:
                return TAG_360P;
            case RESOLUTION_288P:
                return TAG_288P;
            default:
                return TAG_288P;
        }
    }

    // 生成转码操作指令列表
    private static void generateFopList(String resolution, String originalVideo, List<String> avthumbList) {
        String videoEntry = null;
        switch (resolution) {
            case RESOLUTION_SOURCE:
                // 如果是原分辨率则增加1080P, 720P, 480P, 360P, 288P转码
                videoEntry = String.format("%s:/%s/%s", VIDEO_BUCKET, TAG_1080P, originalVideo);
                avthumbList.add(String.format(persistentOpfsTemplate, RESOLUTION_1080P, UrlSafeBase64.encodeToString(videoEntry)));
            case RESOLUTION_1080P:
                // 如果是1080P则增加720P, 480P, 360P, 288P转码
                videoEntry = String.format("%s:/%s/%s", VIDEO_BUCKET, TAG_720P, originalVideo);
                avthumbList.add(String.format(persistentOpfsTemplate, RESOLUTION_720P, UrlSafeBase64.encodeToString(videoEntry)));
            case RESOLUTION_720P:
                // 如果是720P则增加480P, 360P, 288P转码
                videoEntry = String.format("%s:/%s/%s", VIDEO_BUCKET, TAG_480P, originalVideo);
                avthumbList.add(String.format(persistentOpfsTemplate, RESOLUTION_480P, UrlSafeBase64.encodeToString(videoEntry)));
            case RESOLUTION_480P:
                // 如果是480P则增加360P, 288P转码
                videoEntry = String.format("%s:/%s/%s", VIDEO_BUCKET, TAG_360P, originalVideo);
                avthumbList.add(String.format(persistentOpfsTemplate, RESOLUTION_360P, UrlSafeBase64.encodeToString(videoEntry)));
            case RESOLUTION_360P:
                // 如果是360P则增加288P转码
                videoEntry = String.format("%s:/%s/%s", VIDEO_BUCKET, TAG_288P, originalVideo);
                avthumbList.add(String.format(persistentOpfsTemplate, RESOLUTION_288P, UrlSafeBase64.encodeToString(videoEntry)));
            case RESOLUTION_288P:
                // 如果是288P则不进行转码
                break;
        }
    }
}